package com.cn.client;

import java.time.temporal.Temporal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

public class MqFaultStrategyImpl {

    private ConcurrentMap<String /*brokerName*/, FaultItem> nodes = new ConcurrentHashMap<>();
    private ThreadLocalIndex threadLocalIndex = new ThreadLocalIndex();

    public String pickOneAtLeast() {
        List<FaultItem> tmpList = new ArrayList<>();
        for (FaultItem faultItem : this.nodes.values()) {
            tmpList.add(faultItem);
        }
        if (null != tmpList) {
            Collections.shuffle(tmpList);
            Collections.sort(tmpList);
            Integer pos = this.threadLocalIndex.getAndIncrement();
            if (pos < 0) {
                pos = 0;
            }
            int i = pos % tmpList.size();
            return tmpList.get(i).getName();
        }

        return null;
    }

    public void updateFaultItem(String name, long currentLatency, long nodelay) {
        FaultItem faultItem = this.nodes.get(name);
        if (null == faultItem) {
            FaultItem f1 = new FaultItem();
            f1.setName(name);
            f1.setCurrentLatency(currentLatency);
            f1.setStartTimeStamp(System.currentTimeMillis() + nodelay);
            faultItem = this.nodes.putIfAbsent(name, f1);
            if (faultItem != null) {
                faultItem.setCurrentLatency(f1.getCurrentLatency());
                faultItem.setStartTimeStamp(f1.getStartTimeStamp());
            }
        } else {
            faultItem.setCurrentLatency(currentLatency);
            faultItem.setStartTimeStamp(System.currentTimeMillis() + nodelay);
        }
    }

    public void remove(String name) {
        this.remove(name);
    }

    public boolean isAvailable(String brokerName) {
        FaultItem faultItem = this.nodes.get(brokerName);
        if (null != faultItem) {
            return faultItem.isAvailable();
        }
        return true;
    }

    class FaultItem implements Comparable {
        private String name;
        private volatile long startTimeStamp;
        private volatile long currentLatency;

        public boolean isAvailable() {
            return (System.currentTimeMillis() - startTimeStamp) > 0;
        }

        @Override
        public int compareTo(Object o) {
            FaultItem old = (FaultItem) o;
            if (old.currentLatency > this.currentLatency) {
                return 1;
            } else if (old.currentLatency < this.currentLatency) {
                return -1;
            }

            if (old.startTimeStamp < this.startTimeStamp) {
                return 1;
            } else {
                return -1;
            }

        }

        @Override
        public int hashCode() {
            return super.hashCode();
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == this) return true;
            FaultItem old = (FaultItem) obj;
            if (old.getCurrentLatency() != getCurrentLatency()) {
                return false;
            }
            if (old.getStartTimeStamp() != getStartTimeStamp()) {
                return false;
            }
            if (old.getName() == null) {
                return false;
            } else {
                if (old.getName().equals(this.getName())) {
                    return true;
                } else {
                    return false;
                }
            }
        }

        @Override
        public String toString() {
            return super.toString();
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public long getStartTimeStamp() {
            return startTimeStamp;
        }

        public void setStartTimeStamp(long startTimeStamp) {
            this.startTimeStamp = startTimeStamp;
        }

        public long getCurrentLatency() {
            return currentLatency;
        }

        public void setCurrentLatency(long currentLatency) {
            this.currentLatency = currentLatency;
        }
    }

}
